/*
 * Copyright (C) 2012 SeekDeepWithin.com
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

package com.seekdeepwithin.Controls;

import javafx.beans.property.*;
import javafx.beans.value.*;
import javafx.geometry.*;
import javafx.scene.Node;
import javafx.scene.layout.*;

/**
 * User: Jonathan Montiverdi
 * Date: Oct 12, 2012
 *
 * A better split pane node.
 * TODO: Override items that need not be exposed.
 */
public class SplitPanel extends Region implements ChangeListener
{
    private Pane m_Pane;
    private Divider m_Divider;
    private Dimension2D m_FirstSize;
    private BooleanProperty isFirstNodeVisible = new SimpleBooleanProperty ();
    private BooleanProperty isSecondNodeVisible = new SimpleBooleanProperty ();
    private ObjectProperty<Orientation> orientation = new SimpleObjectProperty <> ();
    private ObjectProperty<Node> firstNode = new SimpleObjectProperty <> ();
    private ObjectProperty<Node> secondNode = new SimpleObjectProperty <> ();

    /**
     * Initializes a new split panel.
     */
    @SuppressWarnings ("LeakingThisInConstructor")
    public SplitPanel ()
    {
        this.isFirstNodeVisible.set (true);
        this.isSecondNodeVisible.set (true);
        this.widthProperty ().addListener (this);
        this.heightProperty ().addListener (this);
        this.setOrientation (Orientation.HORIZONTAL);
    }

    /**
     * Sets the node's orientation.
     * @param orientation The orientation of the node.
     */
    public final void setOrientation (Orientation orientation)
    {
        this.m_Pane = null;
        this.m_Divider = null;
        this.getChildren ().clear ();
        this.m_Pane = (orientation == Orientation.HORIZONTAL) ? new HBox () : new VBox ();
        this.getChildren ().add (this.m_Pane);
        this.m_Divider = new Divider (this.m_Pane, orientation);

        if (orientation == Orientation.HORIZONTAL)
        {
            this.m_Divider.setWidth (7);
            HBox.setHgrow (this.m_Divider, Priority.NEVER);
        }
        else
        {
            this.m_Divider.setHeight (7);
            VBox.setVgrow (this.m_Divider, Priority.NEVER);
        }

        this.m_Pane.getChildren ().add (this.m_Divider);
        if (this.getFirstNode () != null)
            this.setFirstNode (this.getFirstNode ());
        if (this.getFirstNode () != null)
            this.setSecondNode (this.getSecondNode ());
        this.orientation.set (orientation);
    }

    /**
     * Gets the node's orientation.
     * @return The orientation of the node.
     */
    public final Orientation getOrientation ()
    {
        return this.orientation.get ();
    }

    /**
     * Gets the orientation property.
     * @return Orientation property.
     */
    public final ObjectProperty<Orientation> orientationProperty ()
    {
        return this.orientation;
    }

    /**
     * Sets the first node.
     * @param node Node to set.
     */
    public final void setFirstNode (Node node)
    {
        this.firstNode.set (node);
        this.m_Pane.getChildren ().add (0, node);
    }

    /**
     * Gets the first node.
     * @return The first node.
     */
    public final Node getFirstNode ()
    {
        return this.firstNode.get ();
    }

    /**
     * Gets the property for the first node.
     * @return The property for the first node.
     */
    public final ObjectProperty<Node> firstNodeProperty ()
    {
        return this.firstNode;
    }

    /**
     * Sets the second node.
     * @param node Node to set.
     */
    public final void setSecondNode (Node node)
    {
        this.secondNode.set (node);
        if (this.getOrientation () == Orientation.HORIZONTAL)
            HBox.setHgrow (node, Priority.ALWAYS);
        else
            VBox.setVgrow (node, Priority.ALWAYS);
        this.m_Pane.getChildren ().add (this.m_Pane.getChildren ().size (), node);
    }

    /**
     * Gets the second node.
     * @return The second node.
     */
    public final Node getSecondNode ()
    {
        return this.secondNode.get ();
    }

    /**
     * Gets the property for the second node.
     * @return The property for the second node.
     */
    public final ObjectProperty<Node> secondNodeProperty ()
    {
        return this.secondNode;
    }

    /**
     * Sets if the first node is visible or not.
     * @param isVisible True to show the first node, otherwise false.
     */
    public final void setIsFirstNodeVisible (Boolean isVisible)
    {
        if (this.getIsFirstNodeVisible () == isVisible) return;
        if (!isVisible)
        {
            this.m_Pane.getChildren ().remove (1);
            this.m_Pane.getChildren ().remove (0);
        }
        else
        {
            this.m_Pane.getChildren ().add (0, this.m_Divider);
            this.m_Pane.getChildren ().add (0, this.getFirstNode ());
        }

        this.isFirstNodeVisible.set (isVisible);
        if (!isVisible && !this.getIsSecondNodeVisible ())
        {
            this.setIsSecondNodeVisible (true);
        }
    }

    /**
     * Gets if the first node is visible or not.
     * @return True if the first node is shown, otherwise false.
     */
    public final Boolean getIsFirstNodeVisible ()
    {
        return this.isFirstNodeVisible.get ();
    }

    /**
     * Gets the property for the first node visibility.
     * @return The property for the first node visibility.
     */
    public final BooleanProperty isFirstNodeVisibleProperty ()
    {
        return this.isFirstNodeVisible;
    }

    /**
     * Sets if the second node is visible or not.
     * @param isVisible True to show the second node, otherwise false.
     */
    public final void setIsSecondNodeVisible (Boolean isVisible)
    {
        if (this.getIsSecondNodeVisible () == isVisible) return;
        if (!isVisible)
        {
            // Save max width and height of first node...
            Region node = (Region)this.getFirstNode ();
            this.m_FirstSize = new Dimension2D (node.getMaxWidth (), node.getMaxHeight ());
            node.setMaxSize (USE_COMPUTED_SIZE, USE_COMPUTED_SIZE);
            // Set grow policy for first node...
            if (this.getOrientation () == Orientation.HORIZONTAL)
                HBox.setHgrow (this.getFirstNode (), Priority.ALWAYS);
            else
                VBox.setVgrow (this.getFirstNode (), Priority.ALWAYS);
            this.m_Pane.getChildren ().remove (2);
            this.m_Pane.getChildren ().remove (1);
        }
        else
        {
            // Restore max width and height of First Node...
            Region node = (Region)this.getFirstNode ();
            node.setMaxSize (this.m_FirstSize.getWidth (), this.m_FirstSize.getHeight ());
            // Set grow policy for first node...
            if (this.getOrientation () == Orientation.HORIZONTAL)
                HBox.setHgrow (this.getFirstNode (), Priority.SOMETIMES);
            else
                VBox.setVgrow (this.getFirstNode (), Priority.SOMETIMES);
            this.m_Pane.getChildren ().add (this.m_Divider);
            this.m_Pane.getChildren ().add (this.getFirstNode ());
        }

        this.isSecondNodeVisible.set (isVisible);
        if (!isVisible && !this.getIsFirstNodeVisible ())
        {
            this.setIsFirstNodeVisible (true);
        }
    }

    /**
     * Gets if the second node is visible or not.
     * @return True if the second node is shown, otherwise false.
     */
    public final Boolean getIsSecondNodeVisible ()
    {
        return this.isSecondNodeVisible.get ();
    }

    /**
     * Gets the property for the second node visibility.
     * @return The property for the second node visibility.
     */
    public final BooleanProperty isSecondNodeVisibleProperty ()
    {
        return this.isSecondNodeVisible;
    }

    @Override
    public void changed (ObservableValue ov, Object t, Object t1)
    {
        if (this.m_Pane != null)
        {
            this.m_Pane.setMinSize (this.getWidth (), this.getHeight ());
            this.m_Pane.setMaxSize (this.getWidth (), this.getHeight ());
        }
    }
}
